package cn.dengta.webapp.base.entity;

import java.util.*;

import cn.dengta.common.model.Bean;
import cn.dengta.common.util.PropertyBean;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import me.codeplayer.util.Assert;
import me.codeplayer.util.JSONUtil;
import me.codeplayer.util.StringUtil;
import me.codeplayer.util.X;
import org.apache.commons.lang3.ArrayUtils;

/**
 * 【事务日志-抽象父类】
 */
@Getter
@Setter
@Accessors(chain = true)
public abstract class AbstractBizLog extends BizEntity {

	private static final long serialVersionUID = 1L;
	//
	protected Long userId;
	/** 操作类型 */
	protected Operation type;
	/** 操作详情（JSON数据） */
	protected String detail;
	/** 备注 */
	protected String remark;
	//
	protected transient String[] details;

	public void init(Long userId, Operation type, String detail, String remark, Date now) {
		this.userId = userId;
		this.type = type;
		this.detail = StringUtil.toString(detail);
		this.remark = StringUtil.toString(remark);
		this.initTime(now);
		super.initActiveStatus();
	}

	public void initDefault() {
		detail = StringUtil.toString(detail);
		remark = StringUtil.toString(remark);
		super.initTime(addTime);
		super.initActiveStatus();
	}

	public AbstractBizLog init(String detail, String remark) {
		this.detail = StringUtil.toString(detail);
		this.remark = StringUtil.toString(remark);
		return this;
	}

	public <T extends AbstractBizLog> T setRemark(String remark) {
		this.remark = StringUtil.toString(remark);
		return X.castType(this);
	}

	public AbstractBizLog init(List<FieldDiff> infos) {
		return init(X.size(infos) > 0 ? JSONUtil.encode(infos) : null, null);
	}

	public <T extends AbstractBizLog> T setDiffs(List<FieldDiff> diffs) {
		this.detail = X.isValid(diffs) ? JSONUtil.encode(diffs) : "";
		return X.castType(this);
	}

	public <T extends AbstractBizLog> T setDiffs(FieldDiff... diffs) {
		return setDiffs(Arrays.asList(diffs));
	}

	public boolean hasDetails() {
		return StringUtil.notEmpty(detail);
	}

	public String[] getDetail_() {
		if (!hasDetails()) {
			return null;
		}
		String[] strs = details;
		if (strs == null) {
			StringBuilder old = new StringBuilder();
			StringBuilder current = new StringBuilder();
			List<FieldDiff> logInfos = JSONUtil.parseArray(detail, FieldDiff.class);
			final String sep = "\\r\\n";
			for (FieldDiff info : logInfos) {
				old.append(info.getLabel()).append(':').append(info.getOld()).append(sep);
				current.append(info.getLabel()).append(':').append(info.getNow()).append(sep);
			}
			details = strs = new String[] { old.toString(), current.toString() };
		}
		return strs;
	}

	public String getOldDetails() {
		return ArrayUtils.get(getDetail_(), FieldDiff.OLD_INDEX);
	}

	public String getCurrentDetails() {
		return ArrayUtils.get(getDetail_(), FieldDiff.CURRENT_INDEX);
	}

	public AbstractBizLog appendRemark(String remark) {
		if (StringUtil.notEmpty(remark)) {
			this.remark = StringUtil.isEmpty(this.remark) ? remark : StringUtil.concat(this.remark, " | ", remark);
		}
		return this;
	}

	@Getter
	@Setter
	@Accessors(chain = true)
	public static class FieldDiff {

		public final static int OLD_INDEX = 0;
		public final static int CURRENT_INDEX = 1;

		// 修改字段 字段描述 旧值 新值
		protected String field, label;
		protected Object old, now;

		public FieldDiff(String field, String label, Object old, Object now) {
			this.field = field;
			this.label = label;
			this.old = old;
			this.now = now;
		}

		public void setHistory(Object old) {
			this.old = old;
		}

		public void setCurrent(Object now) {
			this.now = now;
		}

		public static FieldDiff of(String field, String label, Object old, Object now) {
			return new FieldDiff(field, label, old, now);
		}

		public static FieldDiff of(String field, Integer id, Object old, Object now) {
			return new FieldDiff(field, id.toString(), ofValue(old), ofValue(now));
		}

		/**
		 * 比对两个对象的指定字段，记录差异
		 *
		 * @param old 旧对象
		 * @param updated 新对象
		 * @param fields 比对的字段，格式为：字段|方法,描述。如:name,名称，或:getName(),名称
		 */
		public static List<FieldDiff> diff(Object old, Object updated, String... fields) {
			return diff(old, updated, true, fields);
		}

		/**
		 * 比对两个对象的指定字段，记录差异
		 *
		 * @param old 旧对象
		 * @param updated 新对象
		 * @param fields 比对的字段，格式为：字段|方法,描述。如:name,名称，或:getName(),名称
		 */
		public static List<FieldDiff> diff(Object old, Object updated, boolean hasLabel, String... fields) {
			Assert.isTrue(!hasLabel || (fields.length & 1) == 0, "指定键值的参数个数必须为偶数!");
			LinkedList<FieldDiff> list = new LinkedList<>();
			final PropertyBean asm = PropertyBean.getInstance(old.getClass());
			for (int i = 0; i < fields.length; ) {
				String property = fields[i++], label = hasLabel ? fields[i++] : null;
				Object oldVal = asm.getProperty(old, property),
						updatedVal = asm.getProperty(updated, property);
				if (!Objects.equals(oldVal, updatedVal)) {
					list.add(new FieldDiff(property, label, oldVal, updatedVal));
				}
			}
			return list;
		}

		private static Object ofValue(Object val) {
			return val instanceof Bean ? ((Bean) val).getId() : val;
		}

	}

}
